package crew.blt.handsound.client.webgl.property;

import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Widget;

import crew.blt.handsound.client.webgl.HasJSONStringValue;
import crew.gwt.client.widgets.ClassicLabel;

/**
 * A widget which represent a SceneJS Element's property value.
 * It allow the user to manipulate its value, the value would directly be edited into the JSON tree.
 * 
 * @author blt
 *
 * @param <T> the Type of value 
 * @param <W> the Widget permitting the value manipulation 
 */
public abstract class Property<T,W extends Widget & HasValue<T>> extends Composite implements HasValue<T>, HasJSONStringValue {

	/**
	 * The name of the property, this name must be understood by SceneJS.
	 * This name is display to lets the user now which property is related to.
	 */
	private final String propertyName;
	
	/**
	 * The handler which will be call on property's value change.
	 */
//	private PropertyChangeHandler<T> handler = null;
	private boolean valueChangeHandlerInitialized;	
	
	/**
	 * The widget which represent the value of the property.
	 * It will be display to allow users to manipulate the value.
	 * 
	 * This widget must implement the 
	 * {@link com.google.gwt.user.client.ui.HasValue HasValue} interface.
	 */
	private final W valueWidget;

	/**
	 * Creates the widget.
	 * 
	 * @param propertyName
	 * @param widget
	 */
	protected Property(String propertyName, W widget) {
		this.valueWidget = widget;
		this.propertyName = propertyName;

		HorizontalPanel panel = new HorizontalPanel();
		panel.add(new ClassicLabel(propertyName));
		panel.add(valueWidget);
		initWidget(panel);
		
		valueWidget.addValueChangeHandler(valueChangeHandler);
	}
	
	/**
	 * The handler added to the widget to fire the value editing into the JSON tree.
	 */
	private final ValueChangeHandler<T> valueChangeHandler = new ValueChangeHandler<T>() {

		@Override
		public void onValueChange(ValueChangeEvent<T> event) {
//			if( handler != null ){
//				handler.onPropertyChange(propertyName,valueWidget.getValue());
//			}
			ValueChangeEvent.fire(Property.this, getValue());
		}
	};
	
	public String getPropertyName() {
		return propertyName;
	}

	public W getValueWidget() {
		return valueWidget;
	}

	/**
	 * Get the value of the displayed widget.
	 */
	@Override
	public T getValue() {
		return valueWidget.getValue();
	};

	/**
	 * Get the value of the displayed widget as a Strings.
	 */
	public abstract String getValueAsString();
	
	/**
	 * Set default value.
	 */
	public abstract void setValueByDefault(boolean fireEvent);

	@Override
	public void setValue(T value) {
		setValue(value, false);
	};
	
	@Override
	public void setValue(T value, boolean fireEvents) {
	    T oldValue = getValue();
	    valueWidget.setValue(value);
	    if (fireEvents) {
	      ValueChangeEvent.fireIfNotEqual(this, oldValue, value);
	    }
	};
	
	/**
	 * Get the handler which is fired on value editing.
	 * @return the handler
	 */
//	public PropertyChangeHandler<T> getHandler() {
//		return handler;
//	}


	/**
	 * Set the handler which is fired on value editing.
	 * @param handler
	 */
//	public void setHandler(PropertyChangeHandler<T> handler) {
//		this.handler = handler;
//	}
	
	@Override
	public HandlerRegistration addValueChangeHandler(ValueChangeHandler<T> handler) {
	    // Initialization code
	    if (!valueChangeHandlerInitialized) {
	      valueChangeHandlerInitialized = true;
	      addChangeHandler(new ChangeHandler() {
	        public void onChange(ChangeEvent event) {
	          ValueChangeEvent.fire(Property.this, getValue());
	        }
	      });
	    }
	    return addHandler(handler, ValueChangeEvent.getType());
	}

	public HandlerRegistration addChangeHandler(ChangeHandler handler) {
		return addDomHandler(handler, ChangeEvent.getType());
	}
	
	@Override
	public String getJSONString() {
		return getPropertyName() + ": " + getValueAsString();
	}
}
